/*
* Created on Dec 5, 2012
* Created by Paul Gardner
*
* Copyright 2012 Vuze, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package com.aelitis.azureus.core.pairing.impl;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.security.AlgorithmParameters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.minicastle.crypto.agreement.srp.SRP6Server;
import org.minicastle.crypto.agreement.srp.SRP6VerifierGenerator;
import org.minicastle.crypto.digests.SHA256Digest;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.util.RandomUtils;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import org.gudy.azureus2.core3.util.UrlUtils;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageRequest;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageResponse;
import org.json.simple.JSONObject;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.dht.DHT;
import com.aelitis.azureus.core.dht.nat.DHTNATPuncher;
import com.aelitis.azureus.core.dht.nat.DHTNATPuncherListener;
import com.aelitis.azureus.core.dht.transport.DHTTransportContact;
import com.aelitis.azureus.core.nat.NATTraversalHandler;
import com.aelitis.azureus.core.nat.NATTraverser;
import com.aelitis.azureus.core.pairing.PairedServiceRequestHandler;
import com.aelitis.azureus.core.pairing.impl.PairingManagerImpl.PairedServiceImpl;
import com.aelitis.azureus.core.security.CryptoManager;
import com.aelitis.azureus.core.security.CryptoManagerFactory;
import com.aelitis.azureus.plugins.dht.DHTPlugin;
import com.aelitis.azureus.util.JSONUtils;
public class
PairingManagerTunnelHandler
{
private static final String DEFAULT_IDENTITY = "vuze";
private BigInteger N_3072;
private BigInteger G_3072;
private byte[] SRP_SALT;
private BigInteger SRP_VERIFIER;
private PairingManagerImpl manager;
private AzureusCore core;
private boolean started = false;
private boolean active = false;
private List<DHTNATPuncher> nat_punchers_ipv4 = new ArrayList<DHTNATPuncher>();
private List<DHTNATPuncher> nat_punchers_ipv6 = new ArrayList<DHTNATPuncher>();
private int last_punchers_registered = 0;
private TimerEvent update_event;
private Map<String,Object[]> local_server_map =
new LinkedHashMap<String,Object[]>( 10, 0.75f, true )
{
protected boolean
removeEldestEntry(
Map.Entry<String,Object[]> eldest)
{
return size() > 10;
}
};
private long last_server_create_time;
private long last_server_agree_time;
private int total_servers;
private long last_local_server_create_time;
private long last_local_server_agree_time;
private int total_local_servers;
private static final int MAX_TUNNELS = 10;
private Map<String,PairManagerTunnel> tunnels = new HashMap<String,PairManagerTunnel>();
private String init_fail;
protected
PairingManagerTunnelHandler(
PairingManagerImpl _manager,
AzureusCore _core )
{
manager = _manager;
core = _core;
CryptoManager.SRPParameters params = CryptoManagerFactory.getSingleton().getSRPParameters();
if ( params != null ){
SRP_SALT = params.getSalt();
SRP_VERIFIER = params.getVerifier();
}
}
public void
setSRPPassword(
char[] password )
{
if ( password == null || password.length == 0 ){
SRP_SALT = null;
SRP_VERIFIER = null;
CryptoManagerFactory.getSingleton().setSRPParameters( null, null );
}else{
start();
try{
byte[] I = DEFAULT_IDENTITY.getBytes( "UTF-8" );
byte[] P = new String(password).getBytes( "UTF-8" );
byte[] salt = new byte[16];
RandomUtils.nextSecureBytes( salt );
SRP6VerifierGenerator gen = new SRP6VerifierGenerator();
gen.init( N_3072, G_3072, new SHA256Digest());
BigInteger verifier = gen.generateVerifier( salt, I, P );
CryptoManagerFactory.getSingleton().setSRPParameters( salt, verifier );
SRP_SALT = salt;
SRP_VERIFIER = verifier;
}catch( Throwable e ){
Debug.out( e );
}
}
updateActive();
}
private void
start()
{
synchronized( this ){
if ( started ){
return;
}
started = true;
}
N_3072 = fromHex(
"FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08" +
"8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B" +
"302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9" +
"A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6" +
"49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8" +
"FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
"670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C" +
"180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718" +
"3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D" +
"04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D" +
"B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226" +
"1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
"BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC" +
"E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF" );
G_3072 = BigInteger.valueOf(5);
try{
PluginInterface dht_pi = core.getPluginManager().getPluginInterfaceByClass( DHTPlugin.class );
if ( dht_pi == null ){
throw( new Exception( "DHT Plugin not found" ));
}
DHTPlugin dht_plugin = (DHTPlugin)dht_pi.getPlugin();
if ( !dht_plugin.isEnabled()){
throw( new Exception( "DHT Plugin is disabled" ));
}
DHT[] dhts = dht_plugin.getDHTs();
List<DHTNATPuncher> punchers = new ArrayList<DHTNATPuncher>();
for ( DHT dht: dhts ){
int net = dht.getTransport().getNetwork();
if ( net == DHT.NW_MAIN ){
DHTNATPuncher primary_puncher = dht.getNATPuncher();
if ( primary_puncher != null ){
punchers.add( primary_puncher );
nat_punchers_ipv4.add( primary_puncher );
for ( int i=1;i<=2; i++ ){
DHTNATPuncher puncher = primary_puncher.getSecondaryPuncher();
punchers.add( puncher );
nat_punchers_ipv4.add( puncher );
}
}
}else if ( net == DHT.NW_MAIN_V6 ){
/*
* no point in this atm as we don't support v6 tunnels
DHTNATPuncher puncher = dht.getNATPuncher();
if ( puncher != null ){
punchers.add( puncher );
nat_punchers_ipv6.add( puncher );
puncher = puncher.getSecondaryPuncher();
punchers.add( puncher );
nat_punchers_ipv6.add( puncher );
}
*/
}
}
if ( punchers.size() == 0 ){
throw( new Exception( "No suitable DHT instances available" ));
}
for ( DHTNATPuncher p: punchers ){
p.forceActive( true );
p.addListener(
new DHTNATPuncherListener()
{
public void
rendezvousChanged(
DHTTransportContact rendezvous )
{
System.out.println( "active: " + rendezvous.getString());
synchronized( PairingManagerTunnelHandler.this ){
if ( update_event == null ){
update_event =
SimpleTimer.addEvent(
"PMT:defer",
SystemTime.getOffsetTime( 15*1000 ),
new TimerEventPerformer()
{
public void
perform(
TimerEvent event)
{
synchronized( PairingManagerTunnelHandler.this ){
update_event = null;
}
System.out.println( " updating" );
manager.updateNeeded();
};
});
}
}
}
});
}
core.getNATTraverser().registerHandler(
new NATTraversalHandler()
{
private Map<Long,Object[]> server_map =
new LinkedHashMap<Long,Object[]>( 10, 0.75f, true )
{
protected boolean
removeEldestEntry(
Map.Entry<Long,Object[]> eldest)
{
return size() > 10;
}
};
public int
getType()
{
return( NATTraverser.TRAVERSE_REASON_PAIR_TUNNEL );
}
public String
getName()
{
return( "Pairing Tunnel" );
}
public Map
process(
InetSocketAddress originator,
Map data )
{
if ( SRP_VERIFIER == null || !active ){
return( null );
}
boolean good_request = false;
try{
Map result = new HashMap();
Long session = (Long)data.get( "sid" );
if ( session == null ){
return( null );
}
InetAddress tunnel_originator;
try{
tunnel_originator = InetAddress.getByAddress( (byte[])data.get( "origin"));
}catch( Throwable e ){
Debug.out( "originator decode failed: " + data );
return( null );
}
System.out.println( "PairManagerTunnelHander: incoming message - session=" + session + ", payload=" + data + " from " + tunnel_originator + " via " + originator);
SRP6Server server;
BigInteger B;
synchronized( server_map ){
Object[] entry = server_map.get( session );
if ( entry == null ){
long diff = SystemTime.getMonotonousTime() - last_server_create_time;
if ( diff < 5000 ){
try{
long sleep = 5000 - diff;
System.out.println( "Sleeping for " + sleep + " before starting srp" );
Thread.sleep( sleep );
}catch( Throwable e ){
}
}
server = new SRP6Server();
server.init( N_3072, G_3072, SRP_VERIFIER, new SHA256Digest(), RandomUtils.SECURE_RANDOM );
B = server.generateServerCredentials();
server_map.put( session, new Object[]{ server, B });
last_server_create_time = SystemTime.getMonotonousTime();
total_servers++;
}else{
server = (SRP6Server)entry[0];
B = (BigInteger)entry[1];
}
}
Long op = (Long)data.get( "op" );
if ( op == 1 ){
result.put( "op", 2 );
result.put( "s", SRP_SALT );
result.put( "b", B.toByteArray());
good_request = true;
if ( data.containsKey( "test" )){
manager.recordRequest( "SRP Test", originator.getAddress().getHostAddress(), true );
}
}else if ( op == 3 ){
boolean log_error = true;
try{
long diff = SystemTime.getMonotonousTime() - last_server_agree_time;
if ( diff < 5000 ){
try{
long sleep = 5000 - diff;
System.out.println( "Sleeping for " + sleep + " before completing srp" );
Thread.sleep( sleep );
}catch( Throwable e ){
}
}
BigInteger A = new BigInteger((byte[])data.get( "a" ));
BigInteger serverS = server.calculateSecret( A );
byte[] shared_secret = serverS.toByteArray();
Cipher decipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
byte[] key = new byte[16];
System.arraycopy( shared_secret, 0, key, 0, 16 );
SecretKeySpec secret = new SecretKeySpec( key, "AES");
decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec((byte[])data.get( "enc_iv" )));
byte[] dec = decipher.doFinal( (byte[])data.get( "enc_data" ));
String json_str = new String( dec, "UTF-8" );
if ( !json_str.startsWith( "{" )){
log_error = false;
throw( new Exception( "decode failed" ));
}
JSONObject dec_json = (JSONObject)JSONUtils.decodeJSON( json_str );
String tunnel_url = (String)dec_json.get( "url" );
String service_id = new String((byte[])data.get( "service"), "UTF-8" );
String endpoint_url = (String)dec_json.get( "endpoint");
boolean ok = createTunnel( tunnel_originator, session, service_id, secret, tunnel_url, endpoint_url );
result.put( "op", 4 );
result.put( "status", ok?"ok":"failed" );
good_request = true;
}catch( Throwable e ){
result.put( "op", 4 );
result.put( "status", "failed" );
// filter usual errors on bad agreement
if ( e instanceof BadPaddingException ||
e instanceof IllegalBlockSizeException ){
log_error = false;
}
if ( log_error ){
e.printStackTrace();
}
}finally{
last_server_agree_time = SystemTime.getMonotonousTime();
}
}
return( result );
}finally{
if ( !good_request ){
manager.recordRequest( "SRP", originator.getAddress().getHostAddress(), false );
}
}
}
});
SimpleTimer.addPeriodicEvent(
"pm:tunnel:stats",
30*1000,
new TimerEventPerformer()
{
public void
perform(
TimerEvent event)
{
synchronized( tunnels ){
if ( tunnels.size() > 0 ){
System.out.println( "PairTunnels: " + tunnels.size());
for ( PairManagerTunnel t: tunnels.values()){
System.out.println( "\t" + t.getString());
}
}
}
}
});
}catch( Throwable e ){
Debug.out( e );
init_fail = Debug.getNestedExceptionMessage( e );
manager.updateSRPState();
}
}
protected String
getStatus()
{
if ( init_fail != null ){
return( MessageText.getString( "MyTorrentsView.menu.setSpeed.disabled" ) + ": " + init_fail );
}else if ( !active ){
return( MessageText.getString( "pairing.status.initialising" ) + "..." );
}else if ( SRP_SALT == null ){
return( MessageText.getString( "pairing.srp.pw.req" ));
}else if ( last_punchers_registered == 0 ){
return( MessageText.getString( "pairing.srp.registering" ));
}else{
return( MessageText.getString( "tps.status.available" ));
}
}
protected void
setActive(
boolean a )
{
synchronized( this ){
if ( active == a ){
return;
}
active = a;
}
updateActive();
}
private void
updateActive()
{
manager.updateSRPState();
if ( active && SRP_VERIFIER != null ){
start();
}else{
synchronized( tunnels ){
for ( PairManagerTunnel t: new ArrayList<PairManagerTunnel>( tunnels.values())){
t.destroy();
}
}
synchronized( local_server_map ){
local_server_map.clear();
}
}
List<DHTNATPuncher> punchers = new ArrayList<DHTNATPuncher>();
punchers.addAll( nat_punchers_ipv4 );
punchers.addAll( nat_punchers_ipv6 );
for ( DHTNATPuncher p: punchers ){
p.forceActive( active );
}
}
protected void
updateRegistrationData(
Map<String,Object> payload )
{
int puncher_num = 0;
int num_registered = 0;
for ( DHTNATPuncher nat_ipv4: nat_punchers_ipv4 ){
DHTTransportContact rend = nat_ipv4.getRendezvous();
DHTTransportContact lc = nat_ipv4.getLocalContact();
if ( rend != null && lc != null ){
puncher_num++;
InetSocketAddress rend_address = rend.getTransportAddress();
num_registered++;
payload.put( "rc_v4-" + puncher_num, rend_address.getAddress().getHostAddress() + ":" + rend_address.getPort());
if ( puncher_num == 1 ){
payload.put( "rl_v4", lc.getExternalAddress().getAddress().getHostAddress() + ":" + lc.getAddress().getPort());
}
}
}
puncher_num = 0;
for ( DHTNATPuncher nat_ipv6: nat_punchers_ipv6 ){
DHTTransportContact rend = nat_ipv6.getRendezvous();
DHTTransportContact lc = nat_ipv6.getLocalContact();
if ( rend != null && lc != null ){
puncher_num++;
InetSocketAddress rend_address = rend.getTransportAddress();
num_registered++;
payload.put( "rc_v6-" + puncher_num, rend_address.getAddress().getHostAddress() + ":" + rend_address.getPort());
if ( puncher_num == 1 ){
payload.put( "rl_v6", lc.getExternalAddress().getAddress().getHostAddress() + ":" + lc.getAddress().getPort());
}
}
}
if ( num_registered != last_punchers_registered ){
last_punchers_registered = num_registered;
manager.updateSRPState();
}
}
private BigInteger
fromHex(
String hex )
{
return new BigInteger(1, ByteFormatter.decodeString( hex.replaceAll( " ", "" )));
}
protected boolean
handleLocalTunnel(
TrackerWebPageRequest request,
TrackerWebPageResponse response )
throws IOException
{
start();
if ( SRP_VERIFIER == null || !active ){
throw( new IOException( "Secure pairing is not enabled" ));
}
boolean good_request = false;
try{
// remove /pairing/tunnel/
String url = request.getURL().substring( 16 );
int q_pos = url.indexOf( '?' );
Map<String,String> args = new HashMap<String,String>();
if ( q_pos != -1 ){
String args_str = url.substring( q_pos+1 );
String[] bits = args_str.split( "&" );
for ( String arg: bits ){
String[] x = arg.split( "=" );
if ( x.length == 2 ){
args.put( x[0].toLowerCase(), x[1] );
}
}
url = url.substring( 0, q_pos );
}
if ( url.startsWith( "create" )){
String ac = args.get( "ac" );
String sid = args.get( "sid" );
if ( ac == null || sid == null ){
throw( new IOException( "Access code or service id missing" ));
}
if ( !ac.equals( manager.peekAccessCode())){
throw( new IOException( "Invalid access code" ));
}
PairedServiceImpl ps = manager.getService( sid );
if ( ps == null ){
good_request = true;
throw( new IOException( "Service '" + sid + "' not registered" ));
}
PairedServiceRequestHandler handler = ps.getHandler();
if ( handler == null ){
good_request = true;
throw( new IOException( "Service '" + sid + "' has no handler registered" ));
}
JSONObject json = new JSONObject();
JSONObject result = new JSONObject();
json.put( "result", result );
byte[] ss = new byte[]{ SRP_SALT[0], SRP_SALT[1], SRP_SALT[2], SRP_SALT[3] };
long tunnel_id = RandomUtils.nextSecureAbsoluteLong();
String tunnel_name = Base32.encode( ss ) + "_" + tunnel_id;
synchronized( local_server_map ){
long diff = SystemTime.getMonotonousTime() - last_local_server_create_time;
if ( diff < 5000 ){
try{
long sleep = 5000 - diff;
System.out.println( "Sleeping for " + sleep + " before starting srp" );
Thread.sleep( sleep );
}catch( Throwable e ){
}
}
SRP6Server server = new SRP6Server();
server.init( N_3072, G_3072, SRP_VERIFIER, new SHA256Digest(), RandomUtils.SECURE_RANDOM );
BigInteger B = server.generateServerCredentials();
local_server_map.put( tunnel_name, new Object[]{ server, handler, null, null });
last_local_server_create_time = SystemTime.getMonotonousTime();
total_local_servers++;
result.put( "srp_salt", Base32.encode( SRP_SALT ));
result.put( "srp_b", Base32.encode( B.toByteArray()));
Map<String,String> headers = request.getHeaders();
String host = headers.get( "host" );
// remove port number
int pos = host.lastIndexOf( "]" );
if ( pos != -1 ){
// ipv6 literal
host = host.substring( 0, pos+1 );
}else{
pos = host.indexOf( ':' );
if ( pos != -1 ){
host = host.substring( 0, pos );
}
}
String abs_url = request.getAbsoluteURL().toString();
// unfortunately there is some nasty code that uses a configured tracker
// address as the default host
abs_url = UrlUtils.setHost( new URL( abs_url ), host).toExternalForm();
pos = abs_url.indexOf( "/create" );
String tunnel_url = abs_url.substring(0,pos) + "/id/" + tunnel_name;
result.put( "url", tunnel_url );
}
response.getOutputStream().write( JSONUtils.encodeToJSON( json ).getBytes( "UTF-8" ));
response.setContentType( "application/json; charset=UTF-8" );
response.setGZIP( true );
good_request = true;
return( true );
}else if ( url.startsWith( "id/" )){
String tunnel_name = url.substring( 3 );
Object[] entry;
synchronized( local_server_map ){
entry = local_server_map.get( tunnel_name );
if ( entry == null ){
good_request = true;
throw( new IOException( "Unknown tunnel id" ));
}
}
String srp_a = args.get( "srp_a" );
String enc_data = args.get( "enc_data" );
String enc_iv = args.get( "enc_iv" );
if ( srp_a != null && enc_data != null && enc_iv != null ){
try{
synchronized( local_server_map ){
long diff = SystemTime.getMonotonousTime() - last_local_server_agree_time;
if ( diff < 5000 ){
try{
long sleep = 5000 - diff;
System.out.println( "Sleeping for " + sleep + " before completing srp" );
Thread.sleep( sleep );
}catch( Throwable e ){
}
}
}
JSONObject json = new JSONObject();
JSONObject result = new JSONObject();
json.put( "result", result );
SRP6Server server = (SRP6Server)entry[0];
BigInteger A = new BigInteger( Base32.decode( srp_a ));
BigInteger serverS = server.calculateSecret( A );
byte[] shared_secret = serverS.toByteArray();
Cipher decipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
byte[] key = new byte[16];
System.arraycopy( shared_secret, 0, key, 0, 16 );
SecretKeySpec secret = new SecretKeySpec( key, "AES");
decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec( Base32.decode( enc_iv )));
byte[] dec = decipher.doFinal( Base32.decode( enc_data ));
JSONObject dec_json = (JSONObject)JSONUtils.decodeJSON( new String( dec, "UTF-8" ));
String tunnel_url = (String)dec_json.get( "url" );
if ( !tunnel_url.contains( tunnel_name )){
throw( new IOException( "Invalid tunnel url" ));
}
String endpoint_url = (String)dec_json.get( "endpoint");
entry[2] = secret;
entry[3] = endpoint_url;
result.put( "state", "activated" );
response.getOutputStream().write( JSONUtils.encodeToJSON( json ).getBytes( "UTF-8" ));
response.setContentType( "application/json; charset=UTF-8" );
response.setGZIP( true );
good_request = true;
return( true );
}catch( Throwable e ){
throw( new IOException( Debug.getNestedExceptionMessage( e )));
}finally{
last_local_server_agree_time = SystemTime.getMonotonousTime();
}
}else if ( args.containsKey( "close" )){
synchronized( local_server_map ){
local_server_map.remove( tunnel_name );
}
good_request = true;
return( true );
}else{
PairedServiceRequestHandler request_handler = (PairedServiceRequestHandler)entry[1];
SecretKeySpec secret = (SecretKeySpec)entry[2];
String endpoint_url = (String)entry[3];
if ( secret == null ){
throw( new IOException( "auth not completed" ));
}
byte[] request_data = FileUtil.readInputStreamAsByteArray( request.getInputStream());
try{
byte[] decrypted;
{
byte[] IV = new byte[16];
System.arraycopy( request_data, 0, IV, 0, IV.length );
Cipher decipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec( IV ));
decrypted = decipher.doFinal( request_data, 16, request_data.length-16 );
}
byte[] reply_bytes = request_handler.handleRequest( request.getClientAddress2().getAddress(), endpoint_url, decrypted );
{
Cipher encipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
encipher.init( Cipher.ENCRYPT_MODE, secret );
AlgorithmParameters params = encipher.getParameters ();
byte[] IV = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] enc = encipher.doFinal( reply_bytes );
byte[] rep_bytes = new byte[ IV.length + enc.length ];
System.arraycopy( IV, 0, rep_bytes, 0, IV.length );
System.arraycopy( enc, 0, rep_bytes, IV.length, enc.length );
response.getOutputStream().write( rep_bytes );
response.setContentType( "application/octet-stream" );
good_request = true;
return( true );
}
}catch( Throwable e ){
throw( new IOException( Debug.getNestedExceptionMessage( e )));
}
}
}
throw( new IOException( "Unknown tunnel operation" ));
}finally{
if ( !good_request ){
manager.recordRequest( "SRP", request.getClientAddress2().getAddress().getHostAddress(), false );
}
}
}
private boolean
createTunnel(
InetAddress originator,
long session,
String sid,
SecretKeySpec secret,
String tunnel_url,
String endpoint_url )
{
PairedServiceImpl ps = manager.getService( sid );
if ( ps == null ){
Debug.out( "Service '" + sid + "' not registered" );
return( false );
}
PairedServiceRequestHandler handler = ps.getHandler();
if ( handler == null ){
Debug.out( "Service '" + sid + "' has no handler registered" );
return( false );
}
String key = originator.getHostAddress() + ":" + session + ":" + sid;
synchronized( tunnels ){
PairManagerTunnel existing = tunnels.get( key );
if ( existing != null ){
return( true );
}
if ( tunnels.size() > MAX_TUNNELS ){
long oldest_active = Long.MAX_VALUE;
PairManagerTunnel oldest_tunnel = null;
for ( PairManagerTunnel t: tunnels.values()){
long at = t.getLastActive();
if ( at < oldest_active ){
oldest_active = at;
oldest_tunnel = t;
}
}
oldest_tunnel.destroy();
tunnels.remove( oldest_tunnel.getKey());
}
PairManagerTunnel tunnel = new PairManagerTunnel( this, key, originator, sid, handler, secret, tunnel_url, endpoint_url );
tunnels.put( key, tunnel );
System.out.println( "Created pair manager tunnel: " + tunnel.getString());
}
return( true );
}
protected void
closeTunnel(
PairManagerTunnel tunnel )
{
System.out.println( "Destroyed pair manager tunnel: " + tunnel.getString());
synchronized( tunnels ){
tunnels.remove( tunnel.getKey());
}
}
protected void
generateEvidence(
IndentWriter writer )
{
writer.println( "Tunnel Handler" );
writer.indent();
writer.println( "started=" + started + ", active=" + active );
if ( init_fail != null ){
writer.println( "Init fail: " + init_fail );
}
long now = SystemTime.getMonotonousTime();
writer.println( "total local=" + total_local_servers );
writer.println( "last local create=" + (last_local_server_create_time==0?"<never>":String.valueOf(now-last_local_server_create_time)));
writer.println( "last local agree=" + (last_local_server_agree_time==0?"<never>":String.valueOf(now-last_local_server_agree_time)));
writer.println( "total remote=" + total_servers );
writer.println( "last remote create=" + (last_server_create_time==0?"<never>":String.valueOf(now-last_server_create_time)));
writer.println( "last remote agree=" + (last_server_agree_time==0?"<never>":String.valueOf(now-last_server_agree_time)));
synchronized( tunnels ){
writer.println( "tunnels=" + tunnels.size());
for ( PairManagerTunnel tunnel: tunnels.values()){
writer.println( " " + tunnel.getString());
}
}
try{
writer.println( "IPv4 punchers: " + nat_punchers_ipv4.size());
for ( DHTNATPuncher p: nat_punchers_ipv4 ){
writer.println( " " + p.getStats());
}
writer.println( "IPv6 punchers: " + nat_punchers_ipv6.size());
for ( DHTNATPuncher p: nat_punchers_ipv6 ){
writer.println( " " + p.getStats());
}
}finally{
writer.exdent();
}
}
}